from typing import Any
from flask import jsonify, request, redirect, url_for, flash, render_template, Blueprint
from flask_login import login_required, current_user
from flask_babel import _
from ..configs import get_config
from ..models import UserContainerLink, DevEnvironment, ProxyConfigDict
from ..utils.docker_operator_client import DockerOperatorClient
from ..utils.utils import join_url
from ..services.svc_containers import (
    link_container_and_user,
    get_container_user_id,
)
from ..services.svc_dev_envs import (
    CreateDevEnvLeaderTask,
    create_container_under_dev_env,
)
from ..services.svc_host_machine_info import (
    get_all_host_machine_info,
    get_host_machine_info_by_id,
)
from ..services.svc_snapshots import create_snapshot
from ..services.svc_nginx_proxy import format_nginx_url_prefix
from .rt_user_manage import User
from ..baseapp import tasks_manager

bp_container_mgr = Blueprint("container_mgr", __name__, url_prefix="/container_mgr")

docker_client = DockerOperatorClient()


@bp_container_mgr.route("/dashboard")
@login_required
def dashboard():
    def format_container_user_name(container_id: str | None) -> str:
        if container_id is None:
            return "Unknown Container ID..."
        user_id = get_container_user_id(container_id)
        if user_id is not None:
            return User.query.filter_by(id=user_id).first().username
        else:
            return "admin"

    def format_container_dev_env_info(
        container_id: str | None, container_name: str = ""
    ) -> dict[str, Any]:
        if container_id is None:
            name = "Unknown Container ID..."
        name = _("<ISOLATED>")
        url = "INVALID_URL"
        ports = {}
        ide_port = -1
        ide = "None"
        user_container_link = UserContainerLink.query.filter_by(
            container_id=container_id
        ).first()
        if user_container_link is not None:
            dev_env: DevEnvironment = DevEnvironment.query.filter_by(
                id=user_container_link.dev_env_id
            ).first()
            ports = dev_env.configs["ports"]
            if dev_env is not None:
                name = dev_env.name
                proxy_configs: ProxyConfigDict = dev_env.proxy_configs
                config = get_config()
                port = (
                    proxy_configs["port"]
                    if proxy_configs["type"] == "http-dedicated-port"
                    else config["nginx_listen_port"]
                )
                url = f"${{document.location.protocol}}//${{window.location.hostname}}:{port}/"
                if proxy_configs["type"] == "http-subroute":
                    url = join_url(url, proxy_configs["sub_route"])
                    ide = f"WebApp@Subroute: " + proxy_configs["sub_route"]
                elif proxy_configs["type"] == "http-dedicated-port":
                    # url = join_url(url, dev_env.name)
                    # ide = f"Dedicated Port: " + str(dev_env.ide_port)
                    if port >= 0:
                        ide = f"WebApp@Port " + str(port)
                    else:
                        url = ""
                        ide = "No Web App"
        return {"name": name, "url": url, "ports": ports, "ide": ide}

        # else:

    # Fetch all host machines
    all_host_machines = get_all_host_machine_info()
    containers_list = []
    users_list = []
    host_machines = []
    dev_env_info_list = []
    offline_host_machines = []
    for host in all_host_machines:
        try:
            docker_client = DockerOperatorClient(host_machine_address=host.address)
            containers = docker_client.list_containers(all_containers=True)
            # 根据用户信息，如果用户为admin，则显示所有的container，否则只将对应用户的名称添加进去
            if current_user.username == "admin":
                containers_list.extend(containers)
                user_containers = [container for container in containers]
            else:
                user_containers = [
                    container
                    for container in containers
                    if format_container_user_name(container["id"])
                    == current_user.username
                ]
                containers_list.extend(user_containers)

            dev_env_info_list.extend(
                [
                    format_container_dev_env_info(container["id"], container["name"])
                    for container in user_containers
                ]
            )

            host_machines.extend([host] * len(containers_list))
            users_list.extend(
                [
                    format_container_user_name(container["id"])
                    for container in containers_list
                ]
            )
        except Exception as e:
            print(e)
            flash(
                _("Host Machine") + f" {host.name}@{host.address} " + _("is offline.")
            )
            offline_host_machines.append(host)
            continue
    return render_template(
        "dashboard.html",
        **{
            "containers": containers_list,
            "host_machines": host_machines,
            "dev_env_info_list": dev_env_info_list,
            "users": users_list,
            # "nginx_url": format_nginx_url_prefix(),
        },
    )


@bp_container_mgr.route("/start", methods=["POST"])
@login_required
def start_container_route():
    data: dict = request.json
    host_machine_id = data.get("hostMachineID")
    container_name = data.get("containerName")

    if host_machine_id is None or container_name is None:
        return jsonify({"error": "Missing host_machine_id or container_id"}), 400

    hostmachine_info = get_host_machine_info_by_id(host_machine_id)
    docker_op_cli = DockerOperatorClient(hostmachine_info.address)
    docker_op_cli.start_container(container_name)
    return jsonify({"message": "Container started successfully"}), 200


@bp_container_mgr.route("/stop", methods=["POST"])
@login_required
def stop_container_route():
    data: dict = request.json
    host_machine_id = data.get("hostMachineID")
    container_name = data.get("containerName")

    if host_machine_id is None or container_name is None:
        return jsonify({"error": "Missing host_machine_id or container_id"}), 400

    hostmachine_info = get_host_machine_info_by_id(host_machine_id)
    docker_op_cli = DockerOperatorClient(hostmachine_info.address)
    docker_op_cli.stop_container(container_name)
    return jsonify({"message": "Container stopped successfully"}), 200


# 创建一个容器，注意必须要在已有开发环境的情况下。
@bp_container_mgr.route("/create_container", methods=["POST"])
@login_required
def create_container_route():
    data: dict = request.json
    dev_env_name = data.get("devEnvName")
    container_name = data.get("containerName")
    image_name = data.get("imageName")

    if not dev_env_name:
        return (
            jsonify(
                {"status": "error", "message": "Missing development environment name"}
            ),
            400,
        )
    if not container_name:
        return jsonify({"status": "error", "message": "Missing container name"}), 400
    if not image_name:
        return jsonify({"status": "error", "message": "Missing image name"}), 400

    # 假设有一个方法可以基于快照创建容器

    data, status = create_container_under_dev_env(
        image_name, container_name, dev_env_name
    )
    return (jsonify(data), 400) if not status else (jsonify(data), 200)


@bp_container_mgr.route("/start_create_dev_env", methods=["POST"])
@login_required
def start_create_dev_env_route():
    """
    开始创建一个开发环境
    """
    data: dict = request.json
    dev_env_name_original = data.get("devEnvName")  # Get container name from request
    port_mapping_str = data.get("portMapping", "")
    # allow_docker_ops = data.get("allowDockerOps", T)
    network = data.get("network", "")
    development_env_img_id_str = data.get("developmentEnvImgID")
    # Get proxy type from request
    proxy_type = data.get("proxyType", "http-subroute")
    # Get host ID from request
    host_machine_id = data.get("hostId")
    container_ip = data.get("containerIP", "")

    if dev_env_name_original is None:
        return jsonify({"error": "Missing container name"}), 400
    elif dev_env_name_original.find("--") != -1:
        return (
            jsonify(
                {
                    "error": f"Dev-env name {dev_env_name_original} should never contain `--` symbol!"
                }
            ),
            400,
        )
    if development_env_img_id_str is None:
        return jsonify({"error": "Missing development environment ID"}), 400
    else:
        development_env_img_id = int(development_env_img_id_str)

    if host_machine_id is None:
        return jsonify({"error": "Missing host ID"}), 400

    # dev-env-name
    dev_env_name = f"{current_user.username}--{dev_env_name_original}"

    # Custom port mappings (containerPort --> hostPort)
    port_mappings_dict: dict[str, str] = {}

    for port_mapping_item in port_mapping_str.split(",") if port_mapping_str else []:
        items = port_mapping_item.strip().split(":")
        match len(items):
            case 1:
                port_mappings_dict[items[0]] = items[0]
            case 2:
                port_mappings_dict[items[1]] = items[0]
            case _:
                return (
                    jsonify(
                        {"error": f"Invalid port mapping '{port_mapping_item.strip()}'"}
                    ),
                    400,
                )

    # Assuming create_container function can accept these parameters
    hostmachine_info = get_host_machine_info_by_id(host_machine_id)
    docker_op_cli = DockerOperatorClient(hostmachine_info.address)
    # raise NotImplementedError("Not implemented yet")
    task = CreateDevEnvLeaderTask(
        task_id="",
        host_machine_id=host_machine_id,
        docker_operator_client=docker_op_cli,
        dev_env_img_id=development_env_img_id,
        dev_env_name=dev_env_name,  # Pass container name to create_dev_env
        user_id=current_user.id,  # The user that this task belongs to
        proxy_type=proxy_type,
        network=network,
        container_ip=container_ip,
        port_mapping=port_mappings_dict,
    )
    tasks_manager.submit(task)
    return (
        jsonify(
            {
                "task_id": task.task_id,
                "message": "Development environment started to create...",
            }
        ),
        200,
    )


@bp_container_mgr.route("/create_snapshot", methods=["POST"])
@login_required
def create_snapshot_route():
    data: dict = request.json
    container_name = data.get("containerName")
    snapshot_name = data.get("snapshotName")
    host_machine_id = data.get("hostMachineID")

    if container_name is None:
        return jsonify({"error": "Missing container name"}), 400
    elif snapshot_name is None:
        return jsonify({"error": "Missing snapshot name"}), 400
    elif host_machine_id is None:
        return jsonify({"error": "Missing host machine ID"}), 400

    hostmachine_info = get_host_machine_info_by_id(host_machine_id)
    result = create_snapshot(container_name, snapshot_name, hostmachine_info)

    if result["status"] == "success":
        new_container_id = result["new_container_id"]
        return (
            jsonify(
                {
                    "message": "Snapshot created successfully",
                    "new_container_id": new_container_id,
                }
            ),
            200,
        )
    else:
        return jsonify({"error": result["message"]}), 500


@bp_container_mgr.route("/rename_container", methods=["POST"])
@login_required
def rename_container_route():
    data: dict = request.json
    host_machine_id = data.get("hostMachineID")
    container_name = data.get("containerName")
    new_name = data.get("newName")

    if host_machine_id is None:
        return jsonify({"error": "Missing host_machine_id"}), 400
    elif container_name is None:
        return jsonify({"error": "Missing container_name"}), 400
    elif new_name is None:
        return jsonify({"error": "Missing new_name"}), 400

    hostmachine_info = get_host_machine_info_by_id(host_machine_id)
    docker_op_cli = DockerOperatorClient(hostmachine_info.address)

    success = docker_op_cli.rename_container(container_name, new_name)

    if success:
        return (
            jsonify(
                {
                    "message": f"Container {container_name} renamed to {new_name} successfully"
                }
            ),
            200,
        )
    else:
        return jsonify({"error": "Failed to rename container"}), 500
